package no.ntnu.idi.deid.decision;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import no.ntnu.idi.deid.config.Constants;
import no.ntnu.idi.deid.decision.validators.Filters;
import no.ntnu.idi.deid.documentunits.Document;
import no.ntnu.idi.deid.documentunits.Section;
import no.ntnu.idi.deid.documentunits.Sentence;
import no.ntnu.idi.deid.documentunits.Token;
import no.ntnu.idi.deid.patternmatching.search.Search;
import no.ntnu.idi.deid.preprocessor.Documents;
import no.ntnu.idi.deid.preprocessor.postagger.POSTag;
import no.ntnu.idi.deid.searchresultunits.CategoryTag;
import no.ntnu.idi.deid.searchresultunits.Tag;
import no.ntnu.idi.deid.statistical.tfidf.DocumentFrequencyMain;
import no.ntnu.idi.lemma.Norkompleks;
import no.ntnu.idi.lemma.NorkompleksGroup;

public class SensitivityFinalizer {
	
	private Documents mDocuments;
	private DocumentFrequencyMain mDocumentFrequency;
	
	private SensitivityFinalizer(Documents documents) {
		this.mDocuments = documents;
		run();
	}
	
	private SensitivityFinalizer(Documents documents, DocumentFrequencyMain documentFrequency) {
		mDocuments = documents;
		mDocumentFrequency = documentFrequency;
		run();
	}
	
	public static SensitivityFinalizer runFinalizer(Documents documents) {
		return new SensitivityFinalizer(documents);
	}
	
	public static SensitivityFinalizer runFinalizer(Documents dpp, DocumentFrequencyMain documentFrequency) {
		return new SensitivityFinalizer(dpp, documentFrequency);
	}
	
	public void run() {
		for(Document doc: mDocuments) {
			for (Section section : doc) {
				for(Sentence sentence: section) { 
					sentence.setPhraseListFinalTags();
					sentenceTfIdf(sentence);
					for(Token tok : sentence) {
						
						if(!tok.isSensitivityTagDecided()) {
							tfIdfFiltering(tok);
							setSensitivityBasedOnTags(tok);
							stemUnknownWords(tok);
//							tfIdfUnknownWords(tok);
							setUnknown(tok);
						}
					}
					setSensitiveSentences(sentence);				
				}
			}
		}
	}

	private void setSensitiveSentences(Sentence sentence) {
		if(Filters.isSentenceSensitiv(sentence, Constants.SENTENCE_SENSITIVITY_PERCENTAGE_THRESHOLD)) {
			for(Token t: sentence) {
				if(!t.isSensitiv()) {
					t.setFinalTag(Tag.SENSITIVE_SENTENCE);
				}
			}
		}
	}

	private void tfIdfUnknownWords(Token tok) {
		if(Constants.ENABLE_TOKEN_TF_IDF) {
			if(!tok.isSensitivityTagDecided()) {
				if(!getDocumentFrequency().isTokenIDFAboveThreshold(tok, Constants.TOKEN_IDF_LOWER_THRESHOLD)) {
					tok.setFinalTag(Tag.TF_IDF);
				}
			}
		}
	}

	private void stemUnknownWords(Token tok) {
		if(Constants.ENABLE_STEMMING && !tok.isSensitivityTagDecided()) {
			searchStemmedWord(tok);
		}
	}

	private void setUnknown(Token tok) {
		if(!tok.isSensitivityTagDecided()) {
			tok.setSensetivityTag(true);
		}
	}
	
	private DocumentFrequencyMain getDocumentFrequency() {
		if(mDocumentFrequency == null) {
			mDocumentFrequency = new DocumentFrequencyMain(mDocuments);
		}
		return mDocumentFrequency;
	}
	
	private void sentenceTfIdf(Sentence sentence) {
		if(Constants.ENABLE_SENTENCE_TF_IDF) {
			if(!getDocumentFrequency().isSentenceIDFAboveThreshold(sentence, Constants.SENTENCE_TF_IDF_LOWER_THRESHOLD)) {
				for(Token tok: sentence)
					tok.setFinalTag(Tag.SENTENCE_TF_IDF);
			}
		}
	}
	
	private void searchStemmedWord(Token tok) {
		NorkompleksGroup group = Norkompleks.getInstance().search(tok);
		if(group != null) {
			Set<CategoryTag> hashSet = new HashSet<CategoryTag>();
			for(String str: group.getWordList()) {
				Token newToken = new Token(40000,str);
				newToken.setPOSTag(group.getPos());
				Search.searchToken(newToken);
					for(CategoryTag tag : newToken) {
						hashSet.add(tag);
					}
			}
			if(!hashSet.isEmpty()) {
				tok.addSet(hashSet);
				setSensitivityBasedOnTags(tok);
			}
		}
	}

	private void setSensitivityBasedOnTags(Token token) {
		if(token.hasSearchResult()) {
			if(containsOnly(Tag.YEAR, Tag.ZIPCODE, token.getCategoryTagSet())) {
			Filters.zipCodeAndYearFilter(token);
			}
			if(containsOnly(Tag.NAME, Tag.GEOGRAPHICRELATION, token.getCategoryTagSet())) {
				token.setFinalTag(token.getRandomSearchResultTag());
			}
			nameAndGeoRecognizer(token);
			if(token.searchResultSize() == 1) {
				Iterator<CategoryTag> it = token.iterator();
				CategoryTag ct = (CategoryTag) it.next();
				token.setFinalTag(ct);
			}
			else if (token.searchResultSize() > 1) {
				if(Filters.isAllCategoriesSensitive(token)) {
					token.setFinalTag(token.getRandomSearchResultTag());
					
				}
				else if(Filters.isAllCategoriesInSensitive(token)) {
					token.setFinalTag(token.getRandomSearchResultTag());
				}
			}
			else {
				CategoryTag temp = token.getRandomSearchResultTag();
				token.setFinalTag(temp);
			}
			
		}
	}

	private void tfIdfFiltering(Token token) {
		if (Constants.ENABLE_TOKEN_TF_IDF && !token.isSensitivityTagDecided()) {
			if (!getDocumentFrequency().isTokenIDFAboveThreshold(token,
					Constants.TOKEN_IDF_LOWER_THRESHOLD)) {
				token.setFinalTag(Tag.TF_IDF);
			}
			if (getDocumentFrequency().isTokenIDFAboveThreshold(token,
					Constants.TOKEN_IDF_UPPER_THRESHOLD)) {
				token.setFinalTag(Tag.IDF_SENSITIVE);
			}
		}
	}

	private void nameAndGeoRecognizer(Token tok) {
		Set<CategoryTag> set = tok.getCategoryTagSet();
		if (set.size() > 1	&& (set.contains(new CategoryTag(Tag.NAME)) || set.contains(new CategoryTag(Tag.GEOGRAPHICRELATION)))) {
			if (tok.getPOSTag() == POSTag.NOUN_PROP) {

				// if(Filters.firstLetterIsUpperCase(tok)){
				setNameOrGeo(tok, set);
			} else {
//				try {
//					Token prevToken = tok.getParent().getTokenWithIndex(
//							tok.getIndex() - 1);
//					if (prevToken.getInfoToken()
//							&& (tok.getParent().equals(prevToken.getParent()))) {
//						setNameOrGeo(tok, set);
//					}
//				} catch (ArrayIndexOutOfBoundsException e) {
//					// do nothing
//				}
				Iterator<CategoryTag> it = set.iterator();
				while (it.hasNext()) {
					Tag temp = it.next().getTag();
					if (temp != Tag.NAME
							&& temp != Tag.GEOGRAPHICRELATION) {
						tok.setFinalTag(temp);
						break;
					}
				}
			}
		}
	}

	private void setNameOrGeo(Token tok, Set<CategoryTag> set) {
		if(set.contains(new CategoryTag(Tag.NAME))) {
			tok.setFinalTag(Tag.NAME);
		}
		else {
			tok.setFinalTag(Tag.GEOGRAPHICRELATION);
		}
	}
	
	public static boolean containsOnly(Tag tag1, Tag tag2, Set<CategoryTag> set) {
		if(set.contains(new CategoryTag(tag1)) && set.contains(new CategoryTag(tag2)) && set.size() == 2) {
			return true;
		}
		return false;
	}
		

}
